package com.qf.mybatis01.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
 * 需求
 * 1.如果在web环境下使用该工具类，最好可以让每个线程在调用getSqlSession方法时，能够获取到属于该线程自己的同一个sqlSession对象。
 * 2.避免重复创建对象
 *
 * 该工具类对外提供那些方法？
 *
 * 该工具类在提供一个方法，用于获取指定类型的 Dao层接口的实现类的对象
 * T getMapper(Class<T> clazz)
 */
public class MybatisUtils {

    // 类级别的成员变量 SqlSessionFactory。好处，全局唯一
    private static SqlSessionFactory sqlSessionFactory;

    // 创建 ThreadLocal 对象，该对象用于保存线程私有的数据
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();

    /**
     * 通过静态代码块初始化类级别的成员变量。
     * 静态代码块仅在jvm加载class时执行一次
     */
    static {

        try {
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 获取SqlSession对象
     * @return
     */
    public static SqlSession getSqlSession(){

        // 先检查当前线程是否曾经获取过（创建过）SqlSession对象，如果已经过去过，那么直接使用
        SqlSession sqlSession = threadLocal.get();
        if (sqlSession !=null){
            return sqlSession;
        }
        // 如果当前线程没有获取过SqlSession对象，那么就创建一个新的sqlsession对象，并且保存起来
        sqlSession = sqlSessionFactory.openSession();  // 该方法每次调用都会新创建一个SqlSession对象

        threadLocal.set(sqlSession); // 把新创建的sqlSession对象保存到threadLoacl对象中

        return sqlSession;
    }

    /**
     * 提交事务
     */
    public static void commit(){

        //获取当前线程的sqlsession对象
        final SqlSession sqlSession = getSqlSession();

        sqlSession.commit();

        // 关闭当前线程的sqlSession对象
        closeSqlSession();

    }

    /**
     * 关闭SqlSession
     */
    private static void closeSqlSession() {
        //获取当前线程的sqlsession对象
        final SqlSession sqlSession = getSqlSession();

        // 清理掉threadLocal对象中曾经保存过的sqlsession对象
        threadLocal.remove();

        // 关闭
        sqlSession.close();

    }

    /**
     * 回滚事务
     */
    public static void rollback(){
        //获取当前线程的sqlsession对象
        final SqlSession sqlSession = getSqlSession();

        sqlSession.rollback();

        // 关闭当前线程的sqlSession对象
        closeSqlSession();
    }
    public static <T>  T getMapper(Class<T> clazz){

        //获取当前线程的sqlsession对象
        final SqlSession sqlSession = getSqlSession();

        final T mapper = sqlSession.getMapper(clazz);

        return mapper;

    }


}
